﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using Vux.SVCore.Network;
using Vux.SVCore.Objects;

namespace Vux.SVCore.Server.Network
{
    public class SocketClient
    {
        // keep alive with endpoint
        private Socket _socket = null;
        // used to sync value type variables
        private static object _valueSyncObj = null;
        // used to sync the socket
        private static object _socketSyncObj = null;

        public SocketClient()
        {
            _socketSyncObj = new object();
            _valueSyncObj = new object();
            //_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        }

        public bool IsConnected
        {
            get
            {
                #region Version 1
                //bool v_result = false;
                //if (_socket == null)
                //{
                //    return false;
                //}

                //lock (_socketSyncObj)
                //{
                //    v_result = _socket.Poll(1, SelectMode.SelectRead);
                //}
                //int v_temp;
                //lock (_socketSyncObj)
                //{
                //    v_temp = _socket.Available;
                //}
                ////In order to successfully test to see if the socket is connected, we AND the results of the Poll method and the avialable property.
                //if (v_result == true && v_temp == 0)
                //    return false;
                //return true;
                #endregion
                //var part1 = _socket.Poll(100, SelectMode.SelectRead);
                //var part2 = (_socket.Available == 0);
                //if (part1 & part2)
                //    return false;
                //else
                //    return true;

                return _socket == null ? false : _socket.Connected;
            }
        }

        public bool Connect(IPAddress serverAddress, int port)
        {
            IPEndPoint ep = new IPEndPoint(serverAddress, port);
            return this.Connect(ep);
        }

        public bool Connect(IPEndPoint endPoint)
        {
            try
            {
                CloseConnection();
                _socket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                _socket.Connect(endPoint);
                return true;
            }
            catch (Exception)
            {
                CloseConnection();
                return false;
            }
        }

        public SVCorePacket ReceivePacket()
        {
            var v_pbuffer = ReceiveData(SVCorePacket.HeaderSize);
            var v_packet = SVCorePacket.GetPacketWithOutData(v_pbuffer);
            v_pbuffer = ReceiveData(v_packet.Length);
            v_packet.Data = v_pbuffer;
            return v_packet;
        }

        public byte[] ReceiveData(int length)
        {
            var v_buffer = new byte[0];
            while (length > 0)
            {
                var buffer = new byte[length];
                var payload = _socket.Receive(buffer, length, SocketFlags.None);

                if (payload > 0)
                {
                    var v_dest_index = v_buffer.Length;
                    Array.Resize<byte>(ref v_buffer, v_buffer.Length + payload);
                    Array.Copy(buffer, 0, v_buffer, v_dest_index, payload);
                }
                length -= payload;
            }
            return v_buffer;
        }

        public bool Send(byte[] data)
        {
            if (IsConnected == false)
            {
                return false;
            }
            else
            {
                lock (_socketSyncObj)
                {
                    _socket.Send(data, data.Length, SocketFlags.None);
                }
                return true;
            }
        }

        public void CloseConnection()
        {
            lock (_socketSyncObj)
            {
                if (_socket != null)
                {
                    _socket.Close();
                    _socket = null;
                }
            }
        }
    }
}
